JVM 直接内存溢出

您所在的位置:网站首页 jvm 内存溢出 dump JVM 直接内存溢出

JVM 直接内存溢出

2023-03-17 19:11| 来源: 网络整理| 查看: 265

目录

直接内存

分配直接内存

Unsafe类的使用

ByteBuffer

ByteBuffer源码

总结

在企业开发中可能会遇到这样一种情况,服务器报out of memory异常了,堆Dump文件却看不出问题,这个时候可考虑直接内存溢出问题,也是很容易被忽略的一个点!

直接内存

《Java虚拟机规范》并没有定义直接内存这块区域,且它也不属于语虚拟机运行时数据区

直接内存是一块由操作系统直接管理的内存,也叫堆外内存; 可以使用Unsafe或ByteBuffer分配直接内存; 可用-XX:MaxDirectMemorySize控制,默认是0,表示不限制。

why直接内存?

性能优势 堆内存vs直接内存(参考链接) 

使用场景:

有很大的数据需要存储,生命周期很长 频繁的IO操作,比如并发网络通信 分配直接内存 Unsafe.allocateMemory(size); ByteBuffer.allocateDirect(size); 复制代码

Unsafe类的使用

Unsafe可用来直接访问系统内存资源并自主管理,在提升Java运行效率、增强Java语言底层操作能力方面起了很大的作用——可以认为,Unsafe类是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。

Unsafe不属于Java标准。官方并不建议使用Unsafe,并且从JDK 9开始,官方开始去除Unsafe(issue)。

因此,Unsafe类对于项目实战,意义并不大。然而目前业界有很多好用的类库大量使用了Unsafe类,例如java.util.concurrent.atomic包里的一堆类、Netty、Hadoop,Kafka等。所以了解一下还是有好处的。

不同的JDK版本中,Unsafe类也有区别:

在JDK8中归属于sun.misc包下; 在JDK 11中归属于sun.misc包或jdk.internal.misc下,其中jdk.internal.misc下的Unsafe类功能更强。 package com.wjw.jvminaction; import sun.misc.Unsafe; import java.lang.reflect.Field; public class DirectMemoryTest1 { private static final int MB_1 = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { // 通过反射获取Unsafe类并通过其分配直接内存 Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); // 分配1M内存,并返回这块内存的起始地址 Long address = unsafe.allocateMemory(MB_1); // 向内存地址中设置对象 unsafe.putByte(address, (byte) 1); // 从内存中获取对象 byte aByte = unsafe.getByte(address); System.out.println(aByte); // 释放内存 unsafe.freeMemory(address); } } 复制代码

www.jb51.net/article/140…

Unsafe API介绍及其使用_ahilll的博客-CSDN博客

Java8 Unsafe 解开你神秘的面纱 - 简书

package com.wjw.jvminaction; import sun.misc.Unsafe; import java.lang.reflect.Field; public class DirectMemoryTest3 { private static final int GB_1 = 1024 * 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { // 通过反射获取Unsafe类并通过其分配直接内存 Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); int i = 0; while (true){ unsafe.allocateMemory(GB_1); System.out.println(++ i); } } } 复制代码

 这里也会报OOM的错,但是没有任何的说明信息

使用如下JVM参数启动:

-XX:MaxDirectMemorySize=100m 复制代码

 发现仍然分配了多次,说明MaxDirectMemorySize无法控制Unsafe类

ByteBuffer

ByteBuffer常用方法详解_z69183787的专栏-CSDN博客_bytebuffer使用

package com.wjw.jvminaction; import java.nio.ByteBuffer; public class DirectMemoryTest2 { private static final int ONE_MB = 1024 * 1024; public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocateDirect(ONE_MB); // 相对写,向position的位置写入一个byte,并将postion+1,为下次读写做准备 buffer.put("abcde".getBytes()); buffer.put("fghij".getBytes()); // 转换为读取模式 buffer.flip(); // 相对读,从position位置读取一个byte,并将position+1,为下次读写作准备 // 读取第1个字节(a) System.out.println((char) buffer.get()); // 读取第2个字节(b) System.out.println((char) buffer.get()); // 绝对读,读取byteBuffer底层的bytes中下标为index的byte,不改变position // 读取第3个字节(c) System.out.println((char) buffer.get(2)); } } 复制代码

package com.wjw.jvminaction; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.nio.ByteBuffer; /** * Created by Enzo Cotter on 2021/8/21. */ public class DirectMemoryTest4 { private static final int GB_1 = 1024 * 1024 * 1024; public static void main(String[] args) throws IllegalAccessException { int i = 0; while (true){ ByteBuffer buffer = ByteBuffer.allocateDirect(GB_1); System.out.println(++ i); } } } 复制代码

使用如下JVM参数启动:

-XX:MaxDirectMemorySize=100m 复制代码

发现上来就报错,证明MaxDirectMemorySize可以控制ByteBuffer类,并且报错有提示信息

ByteBuffer源码

进入到 java.nio.ByteBuffer.allocateDirect 方法

public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); } 复制代码

DirectByteBuffer(int cap) { // package-private super(-1, 0, cap, cap); boolean pa = VM.isDirectMemoryPageAligned(); int ps = Bits.pageSize(); long size = Math.max(1L, (long)cap + (pa ? ps : 0)); Bits.reserveMemory(size, cap); long base = 0; try { base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { Bits.unreserveMemory(size, cap); throw x; } unsafe.setMemory(base, size, (byte) 0); if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); att = null; } 复制代码

异常是 Bits.reserveMemory(size, cap); 产生的

static void reserveMemory(long size, int cap) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } // optimist! if (tryReserveMemory(size, cap)) { return; } final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); // retry while helping enqueue pending Reference objects // which includes executing pending Cleaner(s) which includes // Cleaner(s) that free direct buffer memory while (jlra.tryHandlePendingReference()) { if (tryReserveMemory(size, cap)) { return; } } // trigger VM's Reference processing System.gc(); // a retry loop with exponential back-off delays // (this gives VM some time to do it's job) boolean interrupted = false; try { long sleepTime = 1; int sleeps = 0; while (true) { if (tryReserveMemory(size, cap)) { return; } if (sleeps >= MAX_SLEEPS) { break; } if (!jlra.tryHandlePendingReference()) { try { Thread.sleep(sleepTime); sleepTime


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3